package com.nulldev.util.web.YouTubeExtractor.formats;

import java.util.Map;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata.DRMMetadata.DRMFamily;

@SuppressWarnings("deprecation")
public class Formats {

	private static final Map<Integer, Format> FORMATS = FastMaps.map(48);

	/*
	 * DO NOT REMOVE ANY COMMENTED OUT ENTRIES, THESE SERVE AS ARCHIVAL ENTRIES.
	 */

	static {
		if (FORMATS.isEmpty()) {
			// Combined Streams
			/* Removed as of Extractor-1.2/nTube 1.5-rc4 (Jan 2020) */
			FORMATS.put(17, new Format(17, FileType.THREE_GPP_CONTAINER, 30, 144, VideoCodec.MPEG4, AudioCodec.AAC, 24, false));
			FORMATS.put(36, new Format(36, FileType.THREE_GPP_CONTAINER, 30, 240, VideoCodec.MPEG4, AudioCodec.AAC, 32, false));
			FORMATS.put(5, new Format(5, FileType.FLV, 30, 240, VideoCodec.H263, AudioCodec.MPEG3, 64, false));
			FORMATS.put(6, new Format(5, FileType.FLV, 30, 270, VideoCodec.H263, AudioCodec.MPEG3, 64, false));
			FORMATS.put(43, new Format(43, FileType.WEBM, 30, 360, VideoCodec.VP8, AudioCodec.VORBIS, 128, false));
			FORMATS.put(18, new Format(18, FileType.MP4, 30, 360, VideoCodec.H264, AudioCodec.AAC, 96, false));
			FORMATS.put(22, new Format(22, FileType.MP4, 30, 720, VideoCodec.H264, AudioCodec.AAC, 192, false));

			// Separate Streams (partly dash)
			FORMATS.put(160, new Format(160, FileType.MP4, 30, 144, VideoCodec.H264, false));
			FORMATS.put(133, new Format(133, FileType.MP4, 30, 240, VideoCodec.H264, false));
			FORMATS.put(134, new Format(134, FileType.MP4, 30, 360, VideoCodec.H264, false));
			FORMATS.put(135, new Format(135, FileType.MP4, 30, 480, VideoCodec.H264, false));
			FORMATS.put(136, new Format(136, FileType.MP4, 30, 720, VideoCodec.H264, false));
			FORMATS.put(137, new Format(137, FileType.MP4, 30, 1080, VideoCodec.H264, false));
			FORMATS.put(264, new Format(137, FileType.MP4, 30, 1440, VideoCodec.H264, false));
			FORMATS.put(266, new Format(266, FileType.MP4, 30, 2160, VideoCodec.H264, false));
			// 60 FPS
			FORMATS.put(298, new Format(298, FileType.MP4, 60, 720, VideoCodec.H264, false));
			FORMATS.put(299, new Format(299, FileType.MP4, 60, 1080, VideoCodec.H264, false));

			// Extra Stuff that I have no idea why it's a thing (maybe because 25 fps and so
			// forth)
			FORMATS.put(214, new Format(214, FileType.MP4, 25, 720, VideoCodec.H264, false));
			FORMATS.put(216, new Format(216, FileType.MP4, 25, 1080, VideoCodec.H264, false));

			// High Res 60 FPS (That's now a thing btw)
			FORMATS.put(304, new Format(304, FileType.MP4, 60, 1440, VideoCodec.H264, false));
			FORMATS.put(305, new Format(305, FileType.MP4, 60, 2160, VideoCodec.H264, false)); /* registered as 3840x2048, no clue why */

			// Separate Audio (m4a, partly dash)
			FORMATS.put(139, new Format(140, FileType.MP4, AudioCodec.AAC, 48, false));
			FORMATS.put(140, new Format(140, FileType.MP4, AudioCodec.AAC, 128, false));
			FORMATS.put(141, new Format(141, FileType.MP4, AudioCodec.AAC, 256, false));
			// Separate Video (WebM)
			FORMATS.put(278, new Format(278, FileType.WEBM, 30, 144, VideoCodec.VP9, false));
			FORMATS.put(242, new Format(242, FileType.WEBM, 30, 240, VideoCodec.VP9, false));
			FORMATS.put(243, new Format(243, FileType.WEBM, 30, 360, VideoCodec.VP9, false));
			FORMATS.put(244, new Format(244, FileType.WEBM, 30, 480, VideoCodec.VP9, false));
			FORMATS.put(247, new Format(247, FileType.WEBM, 30, 720, VideoCodec.VP9, false));
			FORMATS.put(248, new Format(248, FileType.WEBM, 30, 1080, VideoCodec.VP9, false));
			FORMATS.put(271, new Format(271, FileType.WEBM, 30, 1440, VideoCodec.VP9, false));
			FORMATS.put(313, new Format(313, FileType.WEBM, 30, 2160, VideoCodec.VP9, false));

			// High Quality Separate Streams (720p+ and 60FPS)
			FORMATS.put(302, new Format(302, FileType.WEBM, 60, 720, VideoCodec.VP9, false));
			FORMATS.put(303, new Format(303, FileType.WEBM, 60, 1080, VideoCodec.VP9, false));
			FORMATS.put(308, new Format(308, FileType.WEBM, 60, 1440, VideoCodec.VP9, false));
			FORMATS.put(315, new Format(315, FileType.WEBM, 60, 2160, VideoCodec.VP9, false));
			FORMATS.put(272, new Format(278, FileType.WEBM, 60, 4320, VideoCodec.VP9, false));

			// 10 bit colour streams (yuv420p10)
			FORMATS.put(330, new Format(330, FileType.WEBM, 60, 144, VideoCodec.VP9, true));
			FORMATS.put(331, new Format(331, FileType.WEBM, 60, 240, VideoCodec.VP9, true));
			FORMATS.put(332, new Format(332, FileType.WEBM, 60, 360, VideoCodec.VP9, true));
			FORMATS.put(333, new Format(333, FileType.WEBM, 60, 480, VideoCodec.VP9, true));
			FORMATS.put(334, new Format(334, FileType.WEBM, 60, 720, VideoCodec.VP9, true));
			FORMATS.put(335, new Format(335, FileType.WEBM, 60, 1080, VideoCodec.VP9, true));
			//
			FORMATS.put(337, new Format(337, FileType.WEBM, 60, 4320, VideoCodec.VP9, true));

			// Separate Audio (WebM)
			FORMATS.put(171, new Format(171, FileType.WEBM, AudioCodec.VORBIS, 128, false));
			FORMATS.put(249, new Format(249, FileType.WEBM, AudioCodec.OPUS, 48, false));
			FORMATS.put(250, new Format(250, FileType.WEBM, AudioCodec.OPUS, 64, false));
			FORMATS.put(251, new Format(251, FileType.WEBM, AudioCodec.OPUS, 128, false));

			// HLS live streams (deprecated)
			FORMATS.put(91, new Format(91, FileType.MP4, 30, 144, VideoCodec.H264, AudioCodec.AAC, 48, false));
			FORMATS.put(92, new Format(91, FileType.MP4, 30, 240, VideoCodec.H264, AudioCodec.AAC, 48, false));
			FORMATS.put(93, new Format(93, FileType.MP4, 30, 360, VideoCodec.H264, AudioCodec.AAC, 128, false));
			FORMATS.put(94, new Format(94, FileType.MP4, 30, 480, VideoCodec.H264, AudioCodec.AAC, 128, false));
			FORMATS.put(95, new Format(95, FileType.MP4, 30, 720, VideoCodec.H264, AudioCodec.AAC, 256, false));
			FORMATS.put(95, new Format(95, FileType.MP4, 30, 1080, VideoCodec.H264, AudioCodec.AAC, 256, false));
			// Separate video streams (AV1 codec only)
			FORMATS.put(394, new Format(394, FileType.MP4, 30, 144, VideoCodec.AV1, false));
			FORMATS.put(395, new Format(395, FileType.MP4, 30, 240, VideoCodec.AV1, false));
			FORMATS.put(396, new Format(396, FileType.MP4, 30, 360, VideoCodec.AV1, false));
			FORMATS.put(397, new Format(397, FileType.MP4, 30, 480, VideoCodec.AV1, false));
			FORMATS.put(398, new Format(398, FileType.MP4, 30, 720, VideoCodec.AV1, false));
			FORMATS.put(399, new Format(399, FileType.MP4, 30, 1080, VideoCodec.AV1, false));
			// Separate high quality video streams (AV1) [1440p-8K]
			FORMATS.put(400, new Format(400, FileType.MP4, 30, 1440, VideoCodec.AV1, false));
			FORMATS.put(401, new Format(401, FileType.MP4, 30, 2160, VideoCodec.AV1, false));
			FORMATS.put(402, new Format(402, FileType.MP4, 30, 4320, VideoCodec.AV1, false));
			FORMATS.put(571, new Format(571, FileType.MP4, 60, 4320, VideoCodec.AV1, false));

			// AV1 HDR
			FORMATS.put(694, new Format(694, FileType.MP4, 60, 144, VideoCodec.AV1, true));
			FORMATS.put(695, new Format(695, FileType.MP4, 60, 240, VideoCodec.AV1, true));
			FORMATS.put(696, new Format(696, FileType.MP4, 60, 360, VideoCodec.AV1, true));
			FORMATS.put(697, new Format(697, FileType.MP4, 60, 480, VideoCodec.AV1, true));
			FORMATS.put(698, new Format(698, FileType.MP4, 60, 720, VideoCodec.AV1, true));
			FORMATS.put(699, new Format(699, FileType.MP4, 60, 1080, VideoCodec.AV1, true));
			FORMATS.put(700, new Format(700, FileType.MP4, 60, 1440, VideoCodec.AV1, true));
			FORMATS.put(701, new Format(701, FileType.MP4, 60, 2160, VideoCodec.AV1, true));
			FORMATS.put(702, new Format(702, FileType.MP4, 60, 4320, VideoCodec.AV1, true));
			FORMATS.put(703, new Format(703, FileType.MP4, 60, 4320, VideoCodec.AV1, true));

			// High Quality Audio
			// 6 channels, claimed to be 24000 Hz
			FORMATS.put(256, new Format(256, FileType.MP4_AUDIO, AudioCodec.AAC, 192, false, true));
			// 6 channels, claimed to be 48000 Hz
			FORMATS.put(258, new Format(258, FileType.MP4_AUDIO, AudioCodec.AAC, 384, false, true));

			// Newer ones.
			FORMATS.put(336, new Format(336, FileType.WEBM, 30, 1440, VideoCodec.VP9, true)); // 1440p HDR

			// Subtitles
			// TODO: Implement them.
			FORMATS.put(386, new Format(386, FileType.MP4_TEXT, 0, 0, VideoCodec.NONE, false, SubtitleCodec.MPEG4_PART17));
			FORMATS.put(387, new Format(387, FileType.MP4_TEXT, 0, 0, VideoCodec.NONE, false, SubtitleCodec.MPEG4_PART17));

			// New stuff
			FORMATS.put(599, new Format(599, FileType.MP4_AUDIO, AudioCodec.AAC, 32, false, false)); // 32kbps AAC, lel
			FORMATS.put(600, new Format(600, FileType.WEBM, AudioCodec.OPUS, 32, false, false)); // 32kbps Opus, <Heavy's Voice> No!

			// DRM'd Video
			FORMATS.put(142, new Format(142, FileType.MP4, 30, 240, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(143, new Format(143, FileType.MP4, 30, 360, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(161, new Format(161, FileType.MP4, 30, 144, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(146, new Format(146, FileType.MP4, 30, 1080, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(227, new Format(227, FileType.MP4, 30, 1080, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(226, new Format(226, FileType.MP4, 30, 1080, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(142, new Format(142, FileType.MP4, 30, 480, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(223, new Format(223, FileType.MP4, 30, 480, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(145, new Format(145, FileType.MP4, 30, 720, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(224, new Format(224, FileType.MP4, 30, 720, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(144, new Format(144, FileType.MP4, 30, 480, VideoCodec.H264, false).setDRMProtections(true));
			FORMATS.put(222, new Format(222, FileType.MP4, 30, 480, VideoCodec.H264, false).setDRMProtections(true));

			FORMATS.put(359, new Format(359, FileType.WEBM, 30, 1080, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(658, new Format(658, FileType.MP4, 30, 1080, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(275, new Format(275, FileType.WEBM, 30, 1080, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(657, new Format(657, FileType.MP4, 30, 1080, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(653, new Format(653, FileType.MP4, 30, 720, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(273, new Format(273, FileType.WEBM, 30, 480, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(650, new Format(650, FileType.MP4, 30, 480, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(648, new Format(648, FileType.MP4, 30, 360, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(647, new Format(647, FileType.MP4, 30, 240, VideoCodec.VP9, false).setDRMProtections(true));

			FORMATS.put(317, new Format(317, FileType.WEBM, 30, 480, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(318, new Format(318, FileType.WEBM, 30, 480, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(357, new Format(357, FileType.WEBM, 30, 720, VideoCodec.VP9, false).setDRMProtections(true));
			FORMATS.put(360, new Format(360, FileType.WEBM, 30, 1080, VideoCodec.VP9, false).setDRMProtections(true));

			// Movie Codecs
			FORMATS.put(328, new Format(328, FileType.MP4, AudioCodec.EAC3, 384, false, true).setDRMProtections(true));
			FORMATS.put(325, new Format(325, FileType.MP4, AudioCodec.DCA, 384, false, true).setDRMProtections(true));
			FORMATS.put(380, new Format(380, FileType.MP4, AudioCodec.AC3, 384, false, true).setDRMProtections(true));

			// Full Movie Codecs (with or without DRM)
			FORMATS.put(148, new Format(148, FileType.MP4, AudioCodec.AAC, 48, false, false).setDRMProtections(true));
			FORMATS.put(150, new Format(150, FileType.MP4, AudioCodec.AAC, 256, false, false).setDRMProtections(true));
			FORMATS.put(149, new Format(149, FileType.MP4, AudioCodec.AAC, 128, false, false).setDRMProtections(true));

			FORMATS.put(326, new Format(326, FileType.MP4, AudioCodec.DCA, 384, false, true).setDRMProtections(true));
			FORMATS.put(329, new Format(329, FileType.MP4, AudioCodec.EAC3, 384, false, true).setDRMProtections(true));
			FORMATS.put(381, new Format(381, FileType.MP4, AudioCodec.AC3, 384, false, true).setDRMProtections(true));

			FORMATS.put(350, new Format(350, FileType.WEBM, AudioCodec.OPUS, 48, false, false).setDRMProtections(true));
			FORMATS.put(351, new Format(351, FileType.WEBM, AudioCodec.OPUS, 64, false, false).setDRMProtections(true));
			FORMATS.put(352, new Format(352, FileType.WEBM, AudioCodec.OPUS, 128, false, false).setDRMProtections(true));

			FORMATS.put(689, new Format(689, FileType.MP4, AudioCodec.AAC, 48, false, false).setDRMProtections(true));
			FORMATS.put(688, new Format(688, FileType.MP4, AudioCodec.AAC, 128, false, false).setDRMProtections(true));

			FORMATS.put(691, new Format(691, FileType.MP4, AudioCodec.EAC3, 384, false, true).setDRMProtections(true));

			// Ultra-Low Quality Horrors (24kbps and lower)
			FORMATS.put(597, new Format(597, FileType.MP4, 12, 144, VideoCodec.H264, false).setUltraLowQuality(true));
			FORMATS.put(598, new Format(598, FileType.WEBM, 12, 144, VideoCodec.VP9, false).setUltraLowQuality(true));

			// VP9 in MP4 (m3u8)
			FORMATS.put(602, new Format(602, FileType.MP4, 30, 144, VideoCodec.VP9, false).setUltraLowQuality(true));
			FORMATS.put(603, new Format(603, FileType.MP4, 30, 144, VideoCodec.VP9, false));
			FORMATS.put(604, new Format(604, FileType.MP4, 30, 240, VideoCodec.VP9, false));
			FORMATS.put(605, new Format(605, FileType.MP4, 30, 360, VideoCodec.VP9, false));
			FORMATS.put(606, new Format(606, FileType.MP4, 30, 480, VideoCodec.VP9, false));
			FORMATS.put(609, new Format(609, FileType.MP4, 30, 720, VideoCodec.VP9, false));
			FORMATS.put(614, new Format(614, FileType.MP4, 30, 1080, VideoCodec.VP9, false));

			// Enhanced Bitrate (1080p Premium Ultra-Duper-Super-Enhanced)
			FORMATS.put(616, new Format(616, FileType.MP4, 30, 1080, VideoCodec.VP9, false).setEnhancedBitrate(true));
		}
	}

	private static final Logger log = LoggerFactory.getLoggerD(Arguments.hasArgument("--debug", "--trace", "--Formats.debug"));

	public static Format get(final int itag) {
		final Format f = FORMATS.get(itag);
		if (f == null) {
			log.error("get(int): Format not found! [itag=" + itag + "]");
		}
		return f;
	}

	public static Format auto(final int itag, final Map<String, String> data, final String url) {
		try {
			Format n = null;
			if (data.containsKey("type")) {
				// can auto generate.
				final String[] res = Splitters.splitString(data.getOrDefault("size", "0x0"), "x");
				final int height = MathUtil.safeParseInt(res[1], -1);
				int fps = MathUtil.safeParseInt(data.getOrDefault("fps", "-1"), -1);
				if (fps == 25 || fps == 24) {
					// correct this visually
					fps = 30;
				}
				final String mime = data.get("type").replaceAll(Pattern.quote("%3B"), ";");
				final int bitrate = MathUtil.safeParseInt(data.get("audio_sample_rate"), -1);
				boolean isHQAudio = false;
				if (data.containsKey("audio_channels")) {
					isHQAudio = MathUtil.safeParseInt(data.get("audio_channels"), 2) > 2;
				}
				AudioCodec acodec = AudioCodec.NONE;
				VideoCodec vcodec = VideoCodec.NONE;
				SubtitleCodec scodec = (itag == 387 || itag == 386) ? SubtitleCodec.MPEG4_PART17 : SubtitleCodec.NONE;
				FileType fileType = null;
				if (mime.contains("opus")) {
					acodec = AudioCodec.OPUS;
				} else if (mime.contains("mp4a")) {
					acodec = AudioCodec.AAC;
				} else if (mime.contains("ec-3")) {
					acodec = AudioCodec.EAC3;
				} else if (mime.contains("dtse")) {
					acodec = AudioCodec.DCA;
				} else if (mime.contains("ac-3")) {
					acodec = AudioCodec.AC3;
				} else if (mime.contains("codecs=\"unknown\"")) {
					acodec = AudioCodec.NONE;
				} else if (!mime.startsWith("video/"))
					log.warn("auto(int, Map<String, String>) -> Unknown audioCodec mime: " + mime);
				if (mime.startsWith("video/webm;")) {
					fileType = FileType.WEBM;
				} else if (mime.startsWith("video/mp4;")) {
					fileType = FileType.MP4;
				} else if (mime.startsWith("audio/webm;")) {
					fileType = FileType.WEBM;
				} else if (mime.startsWith("audio/mp4;")) {
					fileType = FileType.MP4_AUDIO;
				} else if (mime.startsWith("text/mp4;")) {
					fileType = FileType.MP4_TEXT;
				} else {
					log.error("auto(int, Map<String, String>) -> Unknown fileType mime: " + mime);
					fileType = FileType.UNKNOWN;
				}
				if (mime.contains("codecs=\"av01")) {
					vcodec = VideoCodec.AV1;
				} else if (mime.contains("codecs=\"av02")) {
					vcodec = VideoCodec.AV2;
				} else if (mime.contains("codecs=\"avc1.")) {
					vcodec = VideoCodec.H264;
				} else if (mime.contains("codecs=\"vp9") || mime.contains("codecs=\"vp09")) {
					vcodec = VideoCodec.VP9;
				} else if (mime.contains("codecs=\"unknown\"")) {
					vcodec = VideoCodec.NONE;
				} else {
					log.error("auto(int, Map<String, String>) -> Unknown mime: " + mime);
					vcodec = VideoCodec.NONE;
				}
				boolean is10bit = false;
				if (FORMATS.containsKey(itag)) {
					is10bit = FORMATS.get(itag).isHDR();
				}
				log.debug("auto(): Generated format for " + data.getOrDefault("size", "0x0") + " @ " + fps + " (itag: " + itag + ")");
				log.debug("primary: " + data.get("primaries"));
				if (!FORMATS.containsKey(itag)) {
					log.warn("auto(int, Map<String, String>): Format not recognised! [itag=" + itag + "]");
					log.warn("URL: " + url);
				}
				return new Format(itag, fileType, fps, height, vcodec, acodec, scodec, bitrate, is10bit, isHQAudio);
			}
			if (n == null) {
				n = FORMATS.get(itag);
			}
			if (n == null) {
				log.warn("auto(int, Map<String, String>): Format not found! [itag=" + itag + "]");
				log.warn("URL: " + url);
			}
			return n;
		} catch (Throwable ex) {
			throw new RuntimeException(ex);
		}
	}

	private static final String[] SDR_PRIMARIES = new String[]
		{ "COLOR_PRIMARIES_BT709" };
	private static final String[] HDR_PRIMARIES = new String[]
		{ "COLOR_PRIMARIES_BT2020" };

	public static Format auto(final int itag, final UniJsonValue data, final String url) {
		try {
			Format n = FORMATS.get(itag);
			if (data.hasValue("mimeType")) {
				final int height = data.getInt("height", 0);
				int fps = data.getInt("fps", -1);
				if (fps == 25 || fps == 24) {
					// correct this visually
					fps = 30;
				}
				final String mime = data.getString("mimeType", "N/A").replaceAll(Pattern.quote("%3B"), ";");
				final int bitrate = n != null ? n.getBitrate() : -1;
				boolean isHQAudio = false;
				if (data.hasValue("audioChannels")) {
					isHQAudio = data.getInt("audioChannels", 2) > 2;
				}
				AudioCodec acodec = AudioCodec.NONE;
				VideoCodec vcodec = VideoCodec.NONE;
				SubtitleCodec scodec = (itag == 387 || itag == 386) ? SubtitleCodec.MPEG4_PART17 : SubtitleCodec.NONE;
				FileType fileType = null;
				if (mime.contains("opus")) {
					acodec = AudioCodec.OPUS;
				} else if (mime.contains("mp4a")) {
					acodec = AudioCodec.AAC;
				} else if (mime.contains("ec-3")) {
					acodec = AudioCodec.EAC3;
				} else if (mime.contains("dtse")) {
					acodec = AudioCodec.DCA;
				} else if (mime.contains("ac-3")) {
					acodec = AudioCodec.AC3;
				} else if (mime.contains("codecs=\"unknown\"")) {
					acodec = AudioCodec.NONE;
				} else if (!mime.startsWith("video/"))
					log.warn("auto(int, UniJsonValue) -> Unknown audioCodec mime: " + mime);
				if (mime.startsWith("video/webm;")) {
					fileType = FileType.WEBM;
				} else if (mime.startsWith("video/mp4;")) {
					fileType = FileType.MP4;
				} else if (mime.startsWith("audio/webm;")) {
					fileType = FileType.WEBM;
				} else if (mime.startsWith("audio/mp4;")) {
					fileType = FileType.MP4_AUDIO;
				} else if (mime.startsWith("text/mp4;")) {
					fileType = FileType.MP4_TEXT;
				} else {
					log.error("auto(int, UniJsonValue) -> Unknown fileType mime: " + mime);
					fileType = FileType.UNKNOWN;
				}
				if (mime.contains("codecs=\"av01")) {
					vcodec = VideoCodec.AV1;
				} else if (mime.contains("codecs=\"av02")) {
					vcodec = VideoCodec.AV2;
				} else if (mime.contains("codecs=\"avc1.")) {
					vcodec = VideoCodec.H264;
				} else if (mime.contains("codecs=\"vp9") || mime.contains("codecs=\"vp09")) {
					vcodec = VideoCodec.VP9;
				} else if (mime.startsWith("audio/") || mime.startsWith("text/")) {
					vcodec = VideoCodec.NONE;
				} else {
					log.error("auto(int, UniJsonValue) -> Unknown videoCodec mime: " + mime);
					vcodec = VideoCodec.NONE;
				}
				boolean is10bit = false;
				if (data.contains("colorInfo")) {
					final String primary = data.get("colorInfo").getString("primaries", null);
					if (primary == null) {
					} else if (ArrayUtils.contains(HDR_PRIMARIES, primary))
						is10bit = true;
					else if (ArrayUtils.contains(SDR_PRIMARIES, primary))
						is10bit = false;
					else {
						log.warn("Unknown color primary: " + primary);
					}
				}
				DRMFamily[] families = new DRMFamily[0];
				if (data.contains("drmFamilies")) {
					final UniJsonValue drf = data.get("drmFamilies");
					families = new DRMFamily[drf.length()];
					for (int i = 0; i < drf.length(); i++) {
						final UniJsonValue s = drf.get(i);
						families[i] = DRMFamily.getFamily(s.toString());
					}
				}
				String trackLang = null, trackID = null;
				boolean isDefaultTrack = true;
				if (data.contains("audioTrack")) {
					final UniJsonValue at = data.get("audioTrack");
					trackLang = at.getString("displayName", null);
					trackID = at.getString("id", null);
					isDefaultTrack = at.bool("audioIsDefault", true);
				}
				int targetLSDuration = -1;
				if (data.contains("targetDurationSec")) {
					targetLSDuration = data.getInt("targetDurationSec", -1);
				}
				log.debug("auto(): Generated format for " + data.getInt("width", 0) + "x" + height + " @ " + fps + " (itag: " + itag + ")");
				if (n == null) {
					log.warn("auto(int, UniJsonValue): Format not recognised! [itag=" + itag + "]");
					log.warn("URL: " + url);
				}
				final boolean isDRC = data.bool("isDrc", false);
				return new Format(itag, fileType, fps, height, vcodec, acodec, scodec, bitrate, is10bit, isHQAudio).addDRMFamilies(families)
						.setTrackLanguage(trackLang).setTrackID(trackID).setDefaultTrack(isDefaultTrack).setLiveStreamTargetDuration(targetLSDuration)
						.setDRCPresence(isDRC);
			}
			if (n == null) {
				log.warn("auto(int, UniJsonValue): Format not found! [itag=" + itag + "]");
				log.warn("URL: " + url);
			}
			return n;
		} catch (Throwable ex) {
			throw new RuntimeException(ex);
		}
	}
}
